/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.loja.framework.hibernate.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.Id;
import br.com.loja.model.dao.IDAO;
import br.com.loja.framework.hibernate.HibernateUtil;
import br.com.loja.util.reflection.ReflectionUtil;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;

/**
 *
 * @author David Luiz
 * @param <T> Classe da entidade a ser persistida
 * @param <PK> Classe correspondente a chave primaria
 */
public abstract class AbstractDAOHibernate<T> implements IDAO<T> {

    private final Class<T> clazz;

    protected AbstractDAOHibernate(Class<T> clazz) {
        this.clazz = clazz;
    }

    public void begin() {
        if (getSession() != null) {
            getSession().beginTransaction();
        }
    }

    public void commit() {
        if (getSession() != null) {
            getSession().getTransaction().commit();
        }
    }

    public void rollback() {
        if (getSession() != null) {
            getSession().getTransaction().rollback();
        }
    }

    public void close() {
        if (getSession() != null) {
            getSession().close();
        }
    }

    public Session getSession() {
        return HibernateUtil.getSession();
    }

    @Override
    public T save(T entity) {

        if (getSession() != null) {

            try {
                begin();
                getSession().merge(entity);
                commit();
            } catch (HibernateException e) {
                //TODO implemetar log 
                System.err.println(e.getMessage());
                rollback();
                e.printStackTrace();
            } finally {
                close();
            }
        }

        return entity;
    }

    @Override
    public List<T> list(boolean cacheable) {
        List<T> entities = null;

        if (getSession() != null) {
            entities = new ArrayList<>();

            try {
                begin();
                Criteria criteria = getSession().createCriteria(clazz).add(Restrictions.eq("dead", false));
                entities = criteria.setCacheable(cacheable).list();
                commit();
            } catch (HibernateException e) {
                //TODO implemetar log
                System.err.println(e.getMessage());
                rollback();
                e.printStackTrace();
            } finally {
                close();
            }
        }

        return entities;
    }

    @Override
    public void remove(T entity) {
        if (getSession() != null) {
            try {
                begin();
                getSession().delete(entity);
                commit();
            } catch (HibernateException e) {
                //TODO implemetar log 
                System.err.println(e.getMessage());
                rollback();
                e.printStackTrace();
            } finally {
                close();
            }
        }
    }

    @Override
    public void update(T entity) {
        if (getSession() != null) {
            try {
                begin();
                getSession().update(entity);
                commit();
            } catch (HibernateException e) {
                //TODO implemetar log 
                System.err.println(e.getMessage());
                rollback();
                e.printStackTrace();
            } finally {
                close();
            }
        }
    }

    @Override
    public T findByPrimaryKey(Object pk) {

        Class<?> type = ReflectionUtil.getFieldByAnnotation(clazz, Id.class).get(0).getType();

        pk = type.cast(pk);

        if (pk == null) {
            throw new IllegalArgumentException("primary key type mismatch");
        }

        T entity = null;
        if (getSession() != null) {
            try {
                begin();
                entity = (T) getSession().createCriteria(clazz)
                        .add(Restrictions.idEq(pk)).setMaxResults(1).uniqueResult();
                commit();
            } catch (HibernateException e) {
                //TODO implemetar log 
                System.err.println(e.getMessage());
                rollback();
                e.printStackTrace();
            } finally {
                close();
            }
        }

        return entity;

    }

    @Override
    public List<T> list(Map<String, ?> properties, boolean cacheable) {
        List<T> entities = null;
        if (getSession() != null) {
            entities = new ArrayList<>();

            if (properties != null) {
                if (!properties.isEmpty()) {

                    try {
                        begin();
                        entities = getSession().createCriteria(clazz).setCacheable(cacheable).add(Restrictions.allEq(properties)).list();
                        commit();
                    } catch (HibernateException e) {
                        //TODO implemetar log 
                        System.err.println(e.getMessage());
                        rollback();
                        e.printStackTrace();
                    } finally {
                        close();
                    }
                }
            }
        }
        return entities;
    }

}
